﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;
using System.Threading.Tasks;
using System.Text;

using RPCComponent;

namespace StorageCleanup
{
	public partial class CleanupLocation : UserControl
	{
		public const String SD = @"D:\StorageCleanerTemp\";

		String path;
		DateTime before;
		String pattern;
		bool justsubs;
		String summary;
		long total;
		bool error;
		String title;
		String message;
		List<FileInfo> files;

		public CleanupLocation (String folderPath, String filePattern, DateTime olderThan, bool subfoldersOnly, String description)
		{
			path = folderPath.Trim();
			pattern = filePattern.Trim();
			before = olderThan;
			justsubs = subfoldersOnly;
			summary = String.IsNullOrEmpty(description) ? folderPath : description;
			total = 0L;
			files = new List<FileInfo>();
			InitializeComponent();
			try
			{
				char last = path[path.Length - 1];
				txtPath.Text = summary;
				if ((last != Path.DirectorySeparatorChar) && (last != Path.AltDirectorySeparatorChar))
				{
					title = path + '\\' + pattern;
				}
				else
				{
					title = path + pattern;
				}
				// Enumerate the directory tree
				if (Directory.Exists(folderPath))
				{
					txtFiles.Text = "???";
					txtSpace.Text = "??? Bytes";
					message = "This folder has not been fully scanned yet.";
					error = false;
				}
				else
				{
					error = true;
					txtFiles.Text = "ERR";
					txtSpace.Text = "not exist";
					message = "Erorr checking path!\nThe folder does not exist!";
				}
			}
			catch (Exception ex)
			{
				error = true;
				txtFiles.Text = "ERR";
				txtSpace.Text = "exception";
				message = "Erorr checking path!\n" + ex.ToString();
			}
		}

		public CleanupLocation (String folderPath) : this(folderPath, "*", DateTime.MaxValue, false, null) { }

		public CleanupLocation (String folderPath, DateTime olderThan) : this(folderPath, "*", olderThan, false, null) { }

		public CleanupLocation (String folderPath, bool subfoldersOnly) : this(folderPath, "*", DateTime.Now, subfoldersOnly, null) { }

		public CleanupLocation (String folderPath, String description) : this(folderPath, "*", DateTime.Now, false, description) { }

		public CleanupLocation (String folderPath, String filePattern, String description) : this(folderPath, filePattern, DateTime.MaxValue, false, description) { }

		public CleanupLocation (String folderPath, bool subfoldersOnly, String description) : this(folderPath, "*", DateTime.MaxValue, subfoldersOnly, description) { }

		public long Compute ()
		{
			total = 0L;
			files.Clear();
			StringBuilder b = new StringBuilder();
			try
			{
				if (error)
					return 0L;
				recurseTree("", b, justsubs);
				message = total + " bytes of matching files:\n" + b.ToString().Trim();
				txtFiles.Text = files.Count.ToString();
				txtSpace.Text = getReadable(total);
				chkCleanup.IsEnabled = true;
			}
			catch (Exception ex)
			{
				error = true;
				message = "Erorr checking directory contents!!\n" + ex.ToString();
				txtFiles.Text = "ERR";
				txtSpace.Text = "exception";
			}
			return total;
		}

		public bool Selected
		{
			get { return (!error && chkCleanup.IsChecked.Value); }
		}

		public int Count
		{
			get { return files.Count; }
		}

		public long Bytes
		{
			get { return total; }
		}

		public String DoCleanup ()
		{
			StringBuilder b = new StringBuilder();
			foreach (FileInfo fi in files)
			{
				try
				{	// Remove Read-Only flag if present
					if (fi.Attributes.HasFlag(FileAttributes.ReadOnly))
						fi.Attributes = FileAttributes.Normal;
					fi.Delete();
					b.AppendLine("Deleted file: " + fi.FullName);
				}
				catch (UnauthorizedAccessException)
				{
					// OK, try this using the move-to-SD trick...
					try
					{
						CRPCComponent.MoveFiles(fi.DirectoryName, fi.Name, SD);
						FileInfo f = new FileInfo(Path.Combine(SD, fi.Name));
						if (f.Attributes.HasFlag(FileAttributes.ReadOnly))
							f.Attributes = FileAttributes.Normal;
						f.Delete();
						b.AppendLine("Deleted using SD: " + fi.FullName);
						Directory.Delete(SD);
					}
					catch (Exception ex)
					{
						b.AppendLine("Failed to delete file (" + ex.GetType().ToString() + "): " + fi.FullName);
					}
				}
				catch (Exception ex)
				{
					b.AppendLine("Failed to delete file (" + ex.GetType().ToString() + "): " + fi.FullName);
				}
			}
			chkCleanup.IsChecked = false;
			return b.ToString().Trim();
		}

		private void recurseTree (String subpath, StringBuilder build, bool ignoreFiles = false)
		{
			// First, add up the files in this dir
			if (!ignoreFiles)
			{
				foreach (String file in Directory.GetFiles(Path.Combine(path, subpath), pattern))
				{
					FileInfo fi = new FileInfo(file);
					// Check the file's age
					// Would use access time instead but it may be disabled on the phone
					if (fi.LastWriteTime < before)
					{
						total += fi.Length;
						build.AppendFormat("{0} : {1}\n",
							Path.Combine(subpath, fi.Name), getReadable(fi.Length));
						files.Add(fi);
					}
				}
			}
			// Next, recurse over the subfolders
			foreach (String sdir in Directory.GetDirectories(Path.Combine(path, subpath)))
			{
				try
				{
					recurseTree(Path.Combine(subpath, Path.GetFileName(sdir)), build);
				}
				catch
				{	// Some folders may not be traversable; just continue
					build.AppendFormat("Warning: Unable to traverse folder {0}!\n", sdir);
				}
			}
		}

		private const long KB = 1000L;
		private const long MB = KB * KB;
		private const long GB = MB * KB;

		public static String getReadable (long l)
		{
			double size;
			String substr;
			if (l > GB)
			{
				// measure in GB
				size = (double)l / GB;
				substr = " GB";
			}
			else if (l > MB)
			{
				size = (double)l / MB;
				substr = " MB";
			}
			else if (l > KB)
			{
				size = (double)l / KB;
				substr = " KB";
			}
			else
			{
				size = (double)l;
				substr = " Bytes";
			}
			String s = size.ToString();
			return ((s.Length > 5) ? s.Substring(0, 5) : s) + substr;
		}

		private void Rectangle_Tap (object sender, System.Windows.Input.GestureEventArgs e)
		{
			String m = message;
			if (message.Length > 1000)
				m = (m.Substring(0, 1000) + "\n\nTruncated...");
			MessageBox.Show(m, title, MessageBoxButton.OK);
		}
	}
}
